package com.okbihuo.perm.core.auth.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.okbihuo.perm.core.auth.jwt.JwtHelper;
import com.okbihuo.perm.core.tool.api.R;
import com.okbihuo.perm.core.tool.api.ResultCode;
import com.okbihuo.perm.core.tool.jackson.JsonUtil;
import com.okbihuo.perm.core.tool.utils.SpringUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 支持把token放在请求参数里，格式为access_token=*****
        String token = request.getParameter(JwtHelper.ACCESS_TOKEN_PARAM);
        if (StrUtil.isBlank(token)) {
            // 支持把token放在请求header里，格式为Basic *******
            String header = request.getHeader(JwtHelper.HEADER);
            if (StrUtil.isNotBlank(header) && header.startsWith(JwtHelper.BASIC_HEADER_PREFIX)) {
                token = header.replace(JwtHelper.BASIC_HEADER_PREFIX, "");
            }
        }
        if (StrUtil.isBlank(token)) {
            chain.doFilter(request, response);
            return;
        }
        try {
//            UsernamePasswordAuthenticationToken 继承 AbstractAuthenticationToken 实现 Authentication
            // 所以当在页面中输/入用户名和密码之后首先会进入到 UsernamePasswordAuthenticationToken验证(Authentication)，
            UsernamePasswordAuthenticationToken authentication = getAuthentication(token);
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);
        } catch (Exception e) {
            R<Object> fail = R.fail(ResultCode.UN_AUTHORIZED, "无效的token");
            ServletUtil.write(response, JsonUtil.toJson(fail), "application/json;charset=UTF-8");
            return;
        }
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(String token) {

        // 失败异常
        Authentication authentication = null;
//            解析token
        Claims claims = Jwts.parser()
                .setSigningKey(JwtHelper.SIGN_KEY)
                .parseClaimsJws(token)
                .getBody();
//            获取username
        String username = claims.getSubject();
//            重新获取用户主体查询权限 TODO 可以优化到缓存
        UserDetailsService bean = SpringUtil.getBean(UserDetailsService.class);
        UserDetails userDetails = bean.loadUserByUsername(username);
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        return authenticationToken;
    }
}
